/*

    Licensed under the Apache License, Version 2.0 (the "License"); 
    you may not use this file except in compliance with the License. 
    You may obtain a copy of the License at 
    http://www.apache.org/licenses/LICENSE-2.0 
    Unless required by applicable law or agreed to in writing, software 
    distributed under the License is distributed on an "AS IS" BASIS,
    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
    See the License for the specific language governing permissions and 
    limitations under the License.
      
 */

package jforex.impl;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.FileLock;
import java.util.Properties;

import jforex.api.ISettings;

/**
 * This is high availability store, implemented using file locks, this souldn't
 * be used for intensive rewriting due to performance reason.
 * 
 * 
 */
class HighAvailabilityStore implements ISettings {

    private String lockFileName = null;

    private FileLock lock = null;

    public HighAvailabilityStore(String lockFile) {

        lockFileName = lockFile;
    }

    private String lock() {

        try {
            // Get a file channel for the file
            File file = new File(lockFileName);
            FileChannel channel = new RandomAccessFile(file, "rw").getChannel();

            // Use the file channel to create a lock on the file.
            // This method blocks until it can retrieve the lock.
            int attempts = 0;
            while (true) {
                try {
                    attempts++;
                    lock = channel.tryLock();
                    if (lock != null) {
                        break;
                    }
                    Thread.sleep(100);
                    if (attempts > 100) {
                        System.out.println("Error Unabel to lock file " + lockFileName);
                        return null;
                    }
                } catch (Exception e) {

                }
            }

            ByteBuffer byteBuffer = ByteBuffer.allocate((int) file.length());
            channel.read(byteBuffer);

            return new String(byteBuffer.array());

        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;

    }

    private void unlock(String string) {
        try {

            FileChannel channel = lock.channel();
            // no changes
            if (string != null) {
                channel.position(0);
                ByteBuffer byteBuffer = ByteBuffer.wrap(string.getBytes());
                channel.write(byteBuffer);
            }
            // Release the lock
            lock.release();
            // Close the file
            channel.close();
            lock = null;
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public void clear() {

        //String props = lock();

        Properties properties = new Properties();

        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        try {
            properties.store(baos, this.getClass().getSimpleName());
        } catch (IOException e) {
            e.printStackTrace();
        }

        unlock(new String(baos.toByteArray()));

    }

    public String get(String key) {

        String props = lock();
        unlock(null);

        Properties properties = new Properties();

        try {
            properties.load(new ByteArrayInputStream(props.getBytes()));
        } catch (IOException e) {
            e.printStackTrace();
        }
        String rc = properties.getProperty(key);
        if (rc != null) {
            rc.trim();
        }
        return rc;

    }

    public String put(String key, String newValue) {

        String oldValue = null;
        if (newValue == null) {
            throw new NullPointerException();
        }

        String props = lock();

        Properties properties = new Properties();

        try {
            properties.load(new ByteArrayInputStream(props.getBytes()));
        } catch (IOException e) {
            e.printStackTrace();
        }

        oldValue = (String) properties.setProperty(key, newValue.trim());

        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        try {
            properties.store(baos, this.getClass().getSimpleName());
        } catch (IOException e) {
            e.printStackTrace();
        }

        unlock(new String(baos.toByteArray()));

        return oldValue;
    }

    public String remove(String key) {

        String oldValue = null;

        String props = lock();

        Properties properties = new Properties();

        try {
            properties.load(new ByteArrayInputStream(props.getBytes()));
        } catch (IOException e) {
            e.printStackTrace();
        }

        oldValue = (String) properties.remove(key);

        unlock(properties.toString());

        return oldValue;
    }

}
